package com.cskaoyan.javase._2polymorphic._3exercise;
/**
 * 读程序题
 * 我们在研究方法调用,以及方法调用的结果时:
 * 要考虑一句话:
 * 编译时(访问范围)看左边,运行时看右边(调用结果看对象)
 *
 * 如果引用的类型是父类类型,那么在编译时,无法直接访问子类独有成员方法,就不需要看子类中独有的方法.
 * 但是如果引用是子类类型,那么在编译时,需要同时考虑父类和子类中的成员方法.
 *
 * 在决定方法调用的结果时,才需要考虑对象的具体类型,尤其是这个方法是子类重写父类时,当对象是子类类型,方法的调用结果一定是子类中的行为.
 *
 * 并且还要注意,在决定调用哪个方法时,如果方法存在方法重载,需要考虑就近原则.
 *
 *
 * @since 17:24
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo2 {
    public static void main(String[] args) {
        /*
        父类引用指向父类对象
            对象和引用的类型都是父类类型
            全程和子类没有关系
            无论是方法的访问范围还是方法的调用结果
            都跑不掉父类A中的两个方法
            完全不需要看子类
         */
        A a1 = new A();
        /*
        父类引用指向子类对象
            从访问范围上来看
            只能访问父类中存在的方法
            但是方法的调用结果还要看
            子类中是否重写该方法
            因为对象是子类对象
         */
        A a2 = new B();
        /*
            子类引用指向子类对象
            所以访问范围要看"父类 + 子类"
            而且方法的调用结果要看子类中的方法实现
         */
        B b = new B();
        C c = new C();
        D d = new D();
        /*
            这个传入B对象,没有恰好能够匹配上的
            需要考虑自动类型转换
            于是匹配传入A对象的方法
            输出AA
          */
        System.out.println("1--" + a1.show(b));
        /*
            这里传入C对象,没有恰好能够匹配上的
            需要考虑自动类型转换
            于是匹配传入A对象的方法
            输出AA
         */
        System.out.println("2--" + a1.show(c));
        /*
            这里传入D对象,恰好能够匹配
            输出AD
         */
        System.out.println("3--" + a1.show(d));

        /*
            这里仍然匹配A类中传入A对象的方法
            但是由于对象是子类对象
            而子类中重写了该方法
            所以输出BA
         */
        System.out.println("4--" + a2.show(b));
        /*
            这里仍然匹配A类中传入A对象的方法
            但是由于对象是子类对象
            而子类中重写了该方法
            所以输出BA
         */
        System.out.println("5--" + a2.show(c));
        /*
            这里直接匹配A类中传入D对象的方法
            输出AD
         */
        System.out.println("6--" + a2.show(d));


        /*
            这里传入B对象
            可以匹配A类中传入A对象的方法
            也可以匹配B类中传入B对象的方法
            B类中的方法不需要类型转换直接匹配
            所以"就近原则"
            应该匹配B类中的方法
            输出BB
         */
        System.out.println("7--" + b.show(b));
        /*
            这里传入C对象
            可以匹配A类中传入A对象的方法
            也可以匹配B类中传入B对象的方法
            A是C的"爷爷"
            B是C的"爸爸"
            显然"爸爸"更近
            依赖就近原则
            应该输出BB
         */
        System.out.println("8--" + b.show(c));

        /*
            传入D对象
            可以匹配A类中传入A对象的方法
            也可以匹配B类中传入B对象的方法
            A是D的"爷爷"
            B是D的"爸爸"
            但是A类中有一个恰好能够直接匹配的传入D对象的方法
            所以就近原则
            输出AD
         */
        System.out.println("9--" + b.show(d));
    }
}

// 四个类中A属于顶层父类
class A {
    // 该方法是父类中独有的方法
    public String show(D obj) {
        return ("A and D");
    }

    // 该方法父子类中都有的方法
    public String show(A obj) {
        return ("A and A");
    }
}

// B是A的直接子类
class B extends A {
    // 该方法是子类中独有的方法
    public String show(B obj) {
        return ("B and B");
    }

    // 该方法是重写自父类的
    @Override
    public String show(A obj) {
        return ("B and A");
    }
}

class C extends B {
}

class D extends B {
}
